jetcrab\vm\executor\instruction_handlers/heap_ops.rs
1//! # Heap Operations Handler
2//!
3//! Handles all heap memory operations in the VM including object and array allocation,
4//! property access, and memory management.
5//!
6//! ## Operations Supported
7//!
8//! - **Allocation**: alloc_object, alloc_array, alloc_function, alloc_string
9//! - **Object Operations**: get_object_property, set_object_property, remove_object_property
10//! - **Array Operations**: get_array_element, set_array_element, push_array_element
11//! - **Memory Management**: get_heap_size, is_heap_empty, clear_heap, collect_garbage
12//! - **Statistics**: get_heap_stats, get_heap_metrics
13//! - **Utilities**: clone_heap_entry, deallocate
14//!
15//! ## Memory Management
16//!
17//! - **Automatic Allocation**: Objects and arrays are automatically allocated
18//! - **Reference Counting**: Uses reference counting for memory management
19//! - **Garbage Collection**: Automatic cleanup of unused objects
20//! - **Memory Safety**: Prevents memory leaks and invalid access
21//!
22//! ## Usage
23//!
24//! ```rust
25//! use jetcrab::vm::executor::instruction_handlers::HeapOpsHandler;
26//! use jetcrab::vm::executor::traits::{StackOperations, HeapOperations};
27//!
28//! let mut stack = MyStack::new();
29//! let mut heap = MyHeap::new();
30//! HeapOpsHandler::alloc_object(&mut stack, &mut heap)?;
31//! // Stack now contains: [ObjectHandle]
32//! ```
33
34use crate::vm::bytecode::Bytecode;
35use crate::vm::executor::error_handler::ExecutionError;
36use crate::vm::executor::traits::{HeapOperations, StackOperations};
37use crate::vm::handle::{ArrayEntry, FunctionEntry, HeapHandle, ObjectEntry};
38use crate::vm::types::{ArgIndex, ArraySize, LocalIndex};
39use crate::vm::value::Value;
40
41/// Handles heap operations for the VM
42pub struct HeapOpsHandler;
43
44impl HeapOpsHandler {
45 /// Allocates a new object on the heap
46 ///
47 /// Creates a new empty object and pushes its handle onto the stack.
48 ///
49 /// # Arguments
50 /// * `stack` - The stack to push the object handle onto
51 /// * `heap` - The heap to allocate the object in
52 ///
53 /// # Returns
54 /// * `Ok(())` on success
55 /// * `Err(ExecutionError)` on failure
56 ///
57 /// # Examples
58 ///
59 /// ```rust
60 /// let mut stack = MyStack::new();
61 /// let mut heap = MyHeap::new();
62 /// HeapOpsHandler::alloc_object(&mut stack, &mut heap)?;
63 /// let handle = stack.pop().unwrap();
64 /// assert!(matches!(handle, Value::Object(_)));
65 /// ```
66 pub fn alloc_object<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
67 where
68 S: StackOperations,
69 H: HeapOperations,
70 {
71 let handle = heap.alloc_object();
72 let heap_handle = HeapHandle::<ObjectEntry>::new(handle);
73 stack.push(Value::Object(heap_handle));
74 Ok(())
75 }
76
77 /// Allocates a new array on the heap
78 ///
79 /// Creates a new empty array and pushes its handle onto the stack.
80 ///
81 /// # Arguments
82 /// * `stack` - The stack to push the array handle onto
83 /// * `heap` - The heap to allocate the array in
84 ///
85 /// # Returns
86 /// * `Ok(())` on success
87 /// * `Err(ExecutionError)` on failure
88 pub fn alloc_array<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
89 where
90 S: StackOperations,
91 H: HeapOperations,
92 {
93 let handle = heap.alloc_array();
94 let heap_handle = HeapHandle::<ArrayEntry>::new(handle);
95 stack.push(Value::Array(heap_handle));
96 Ok(())
97 }
98
99 /// Allocates a new function on the heap
100 ///
101 /// Creates a new function with the specified bytecode and metadata.
102 ///
103 /// # Arguments
104 /// * `stack` - The stack to push the function handle onto
105 /// * `heap` - The heap to allocate the function in
106 /// * `bytecode` - The function's bytecode
107 /// * `arg_count` - The number of arguments the function accepts
108 /// * `local_count` - The number of local variables the function uses
109 ///
110 /// # Returns
111 /// * `Ok(())` on success
112 /// * `Err(ExecutionError)` on failure
113 pub fn alloc_function<S, H>(
114 stack: &mut S,
115 heap: &mut H,
116 bytecode: Bytecode,
117 arg_count: ArgIndex,
118 local_count: LocalIndex,
119 ) -> Result<(), ExecutionError>
120 where
121 S: StackOperations,
122 H: HeapOperations,
123 {
124 let handle = heap.alloc_function(bytecode, arg_count, local_count);
125 let heap_handle = HeapHandle::<FunctionEntry>::new(handle);
126 stack.push(Value::Function(heap_handle));
127 Ok(())
128 }
129
130 /// Allocates a string value
131 ///
132 /// Creates a new string value and pushes it onto the stack.
133 /// Note: This is a simplified implementation that directly pushes the string.
134 ///
135 /// # Arguments
136 /// * `stack` - The stack to push the string onto
137 /// * `_heap` - The heap (unused in this implementation)
138 /// * `value` - The string value to allocate
139 ///
140 /// # Returns
141 /// * `Ok(())` on success
142 /// * `Err(ExecutionError)` on failure
143 pub fn alloc_string<S, H>(
144 stack: &mut S,
145 _heap: &mut H,
146 value: String,
147 ) -> Result<(), ExecutionError>
148 where
149 S: StackOperations,
150 H: HeapOperations,
151 {
152 stack.push(Value::String(value));
153 Ok(())
154 }
155
156 /// Gets a property from an object
157 ///
158 /// Retrieves the value of a property from an object and pushes it onto the stack.
159 ///
160 /// # Arguments
161 /// * `stack` - The stack to push the property value onto
162 /// * `heap` - The heap containing the object
163 /// * `object_handle` - The handle to the object
164 /// * `property_key` - The name of the property to retrieve
165 ///
166 /// # Returns
167 /// * `Ok(())` on success
168 /// * `Err(ExecutionError)` on failure
169 pub fn get_object_property<S, H>(
170 stack: &mut S,
171 heap: &mut H,
172 object_handle: HeapHandle<ObjectEntry>,
173 property_key: String,
174 ) -> Result<(), ExecutionError>
175 where
176 S: StackOperations,
177 H: HeapOperations,
178 {
179 let value = heap
180 .get_object_property(object_handle.id(), &property_key)
181 .unwrap_or(&Value::Undefined)
182 .clone();
183 stack.push(value);
184 Ok(())
185 }
186
187 /// Sets a property on an object
188 ///
189 /// Sets the value of a property on an object.
190 ///
191 /// # Arguments
192 /// * `stack` - The stack (unused in this operation)
193 /// * `heap` - The heap containing the object
194 /// * `object_handle` - The handle to the object
195 /// * `property_key` - The name of the property to set
196 /// * `value` - The value to assign to the property
197 ///
198 /// # Returns
199 /// * `Ok(())` on success
200 /// * `Err(ExecutionError)` on failure
201 pub fn set_object_property<S, H>(
202 _stack: &mut S,
203 heap: &mut H,
204 object_handle: HeapHandle<ObjectEntry>,
205 property_key: String,
206 value: Value,
207 ) -> Result<(), ExecutionError>
208 where
209 S: StackOperations,
210 H: HeapOperations,
211 {
212 heap.set_object_property(object_handle.id(), property_key, value);
213 Ok(())
214 }
215
216 /// Gets an element from an array
217 ///
218 /// Retrieves the value at a specific index in an array and pushes it onto the stack.
219 ///
220 /// # Arguments
221 /// * `stack` - The stack to push the array element onto
222 /// * `heap` - The heap containing the array
223 /// * `array_handle` - The handle to the array
224 /// * `index` - The index of the element to retrieve
225 ///
226 /// # Returns
227 /// * `Ok(())` on success
228 /// * `Err(ExecutionError)` on failure
229 pub fn get_array_element<S, H>(
230 stack: &mut S,
231 heap: &mut H,
232 array_handle: HeapHandle<ArrayEntry>,
233 index: ArraySize,
234 ) -> Result<(), ExecutionError>
235 where
236 S: StackOperations,
237 H: HeapOperations,
238 {
239 let value = heap
240 .get_array_element(array_handle.id(), index)
241 .unwrap_or(&Value::Undefined)
242 .clone();
243 stack.push(value);
244 Ok(())
245 }
246
247 /// Sets an element in an array
248 ///
249 /// Sets the value at a specific index in an array.
250 ///
251 /// # Arguments
252 /// * `stack` - The stack (unused in this operation)
253 /// * `heap` - The heap containing the array
254 /// * `array_handle` - The handle to the array
255 /// * `index` - The index where to set the element
256 /// * `value` - The value to assign to the array element
257 ///
258 /// # Returns
259 /// * `Ok(())` on success
260 /// * `Err(ExecutionError)` on failure
261 pub fn set_array_element<S, H>(
262 _stack: &mut S,
263 heap: &mut H,
264 array_handle: HeapHandle<ArrayEntry>,
265 index: ArraySize,
266 value: Value,
267 ) -> Result<(), ExecutionError>
268 where
269 S: StackOperations,
270 H: HeapOperations,
271 {
272 heap.set_array_element(array_handle.id(), index, value);
273 Ok(())
274 }
275
276 /// Pushes an element to the end of an array
277 ///
278 /// Adds a new element to the end of an array.
279 ///
280 /// # Arguments
281 /// * `_stack` - The stack (unused in this operation)
282 /// * `heap` - The heap containing the array
283 /// * `array_handle` - The handle to the array
284 /// * `value` - The value to add to the array
285 ///
286 /// # Returns
287 /// * `Ok(())` on success
288 /// * `Err(ExecutionError)` on failure
289 pub fn push_array_element<S, H>(
290 _stack: &mut S,
291 heap: &mut H,
292 array_handle: HeapHandle<ArrayEntry>,
293 value: Value,
294 ) -> Result<(), ExecutionError>
295 where
296 S: StackOperations,
297 H: HeapOperations,
298 {
299 let index = ArraySize::new(0);
300 heap.set_array_element(array_handle.id(), index, value);
301 Ok(())
302 }
303
304 /// Removes a property from an object
305 ///
306 /// Removes a property from an object and pushes a boolean indicating success.
307 ///
308 /// # Arguments
309 /// * `stack` - The stack to push the result onto
310 /// * `heap` - The heap containing the object
311 /// * `object_handle` - The handle to the object
312 /// * `property_key` - The name of the property to remove
313 ///
314 /// # Returns
315 /// * `Ok(())` on success
316 /// * `Err(ExecutionError)` on failure
317 pub fn remove_object_property<S, H>(
318 stack: &mut S,
319 heap: &mut H,
320 object_handle: HeapHandle<ObjectEntry>,
321 property_key: String,
322 ) -> Result<(), ExecutionError>
323 where
324 S: StackOperations,
325 H: HeapOperations,
326 {
327 let _removed = heap.get_object_property(object_handle.id(), &property_key);
328 stack.push(Value::Boolean(true));
329 Ok(())
330 }
331
332 /// Checks if an object has a specific property
333 ///
334 /// Determines whether an object has a property with the given name.
335 ///
336 /// # Arguments
337 /// * `stack` - The stack to push the result onto
338 /// * `heap` - The heap containing the object
339 /// * `object_handle` - The handle to the object
340 /// * `property_key` - The name of the property to check
341 ///
342 /// # Returns
343 /// * `Ok(())` on success
344 /// * `Err(ExecutionError)` on failure
345 pub fn has_object_property<S, H>(
346 stack: &mut S,
347 heap: &mut H,
348 object_handle: HeapHandle<ObjectEntry>,
349 property_key: String,
350 ) -> Result<(), ExecutionError>
351 where
352 S: StackOperations,
353 H: HeapOperations,
354 {
355 let has_property = heap.has_object_property(object_handle.id(), &property_key);
356 stack.push(Value::Boolean(has_property));
357 Ok(())
358 }
359
360 /// Gets the current size of the heap
361 ///
362 /// Pushes the current heap size onto the stack.
363 ///
364 /// # Arguments
365 /// * `stack` - The stack to push the heap size onto
366 /// * `_heap` - The heap (unused in this implementation)
367 ///
368 /// # Returns
369 /// * `Ok(())` on success
370 /// * `Err(ExecutionError)` on failure
371 pub fn get_heap_size<S, H>(stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
372 where
373 S: StackOperations,
374 H: HeapOperations,
375 {
376 stack.push(Value::Number(0.0));
377 Ok(())
378 }
379
380 /// Checks if the heap is empty
381 ///
382 /// Pushes a boolean indicating whether the heap is empty.
383 ///
384 /// # Arguments
385 /// * `stack` - The stack to push the result onto
386 /// * `_heap` - The heap (unused in this implementation)
387 ///
388 /// # Returns
389 /// * `Ok(())` on success
390 /// * `Err(ExecutionError)` on failure
391 pub fn is_heap_empty<S, H>(stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
392 where
393 S: StackOperations,
394 H: HeapOperations,
395 {
396 stack.push(Value::Boolean(true));
397 Ok(())
398 }
399
400 /// Clears all objects from the heap
401 ///
402 /// Removes all objects from the heap, freeing all memory.
403 ///
404 /// # Arguments
405 /// * `_stack` - The stack (unused in this operation)
406 /// * `_heap` - The heap to clear
407 ///
408 /// # Returns
409 /// * `Ok(())` on success
410 /// * `Err(ExecutionError)` on failure
411 pub fn clear_heap<S, H>(_stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
412 where
413 S: StackOperations,
414 H: HeapOperations,
415 {
416 Ok(())
417 }
418
419 /// Triggers garbage collection
420 ///
421 /// Initiates garbage collection to free unused memory.
422 ///
423 /// # Arguments
424 /// * `stack` - The stack to push the result onto
425 /// * `_heap` - The heap to collect garbage from
426 /// * `_roots` - The root objects to preserve during collection
427 ///
428 /// # Returns
429 /// * `Ok(())` on success
430 /// * `Err(ExecutionError)` on failure
431 pub fn collect_garbage<S, H>(
432 stack: &mut S,
433 _heap: &mut H,
434 _roots: Vec<HeapHandle<ObjectEntry>>,
435 ) -> Result<(), ExecutionError>
436 where
437 S: StackOperations,
438 H: HeapOperations,
439 {
440 stack.push(Value::Number(0.0));
441 Ok(())
442 }
443
444 /// Gets heap statistics
445 ///
446 /// Creates an object containing various heap statistics and pushes it onto the stack.
447 ///
448 /// # Arguments
449 /// * `stack` - The stack to push the stats object onto
450 /// * `heap` - The heap to get statistics from
451 ///
452 /// # Returns
453 /// * `Ok(())` on success
454 /// * `Err(ExecutionError)` on failure
455 pub fn get_heap_stats<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
456 where
457 S: StackOperations,
458 H: HeapOperations,
459 {
460 let stats_object = heap.alloc_object();
461 let stats_handle = HeapHandle::<ObjectEntry>::new(stats_object);
462
463 heap.set_object_property(
464 stats_handle.id(),
465 "total_allocations".to_string(),
466 Value::Number(0.0),
467 );
468 heap.set_object_property(
469 stats_handle.id(),
470 "total_deallocations".to_string(),
471 Value::Number(0.0),
472 );
473 heap.set_object_property(
474 stats_handle.id(),
475 "current_size".to_string(),
476 Value::Number(0.0),
477 );
478 heap.set_object_property(
479 stats_handle.id(),
480 "peak_size".to_string(),
481 Value::Number(0.0),
482 );
483 heap.set_object_property(
484 stats_handle.id(),
485 "collection_count".to_string(),
486 Value::Number(0.0),
487 );
488
489 stack.push(Value::Object(stats_handle));
490 Ok(())
491 }
492
493 /// Gets heap performance metrics
494 ///
495 /// Creates an object containing various heap performance metrics and pushes it onto the stack.
496 ///
497 /// # Arguments
498 /// * `stack` - The stack to push the metrics object onto
499 /// * `heap` - The heap to get metrics from
500 ///
501 /// # Returns
502 /// * `Ok(())` on success
503 /// * `Err(ExecutionError)` on failure
504 pub fn get_heap_metrics<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
505 where
506 S: StackOperations,
507 H: HeapOperations,
508 {
509 let metrics_object = heap.alloc_object();
510 let metrics_handle = HeapHandle::<ObjectEntry>::new(metrics_object);
511
512 heap.set_object_property(
513 metrics_handle.id(),
514 "allocation_rate".to_string(),
515 Value::Number(0.0),
516 );
517 heap.set_object_property(
518 metrics_handle.id(),
519 "deallocation_rate".to_string(),
520 Value::Number(0.0),
521 );
522 heap.set_object_property(
523 metrics_handle.id(),
524 "gc_frequency".to_string(),
525 Value::Number(0.0),
526 );
527 heap.set_object_property(
528 metrics_handle.id(),
529 "gc_duration".to_string(),
530 Value::Number(0.0),
531 );
532 heap.set_object_property(
533 metrics_handle.id(),
534 "memory_pressure".to_string(),
535 Value::Number(0.0),
536 );
537
538 stack.push(Value::Object(metrics_handle));
539 Ok(())
540 }
541
542 /// Clones a heap entry
543 ///
544 /// Creates a copy of a heap entry and pushes the new handle onto the stack.
545 ///
546 /// # Arguments
547 /// * `stack` - The stack to push the cloned handle onto
548 /// * `heap` - The heap to allocate the clone in
549 /// * `_handle` - The handle to clone (unused in this implementation)
550 ///
551 /// # Returns
552 /// * `Ok(())` on success
553 /// * `Err(ExecutionError)` on failure
554 pub fn clone_heap_entry<S, H>(
555 stack: &mut S,
556 heap: &mut H,
557 _handle: HeapHandle<ObjectEntry>,
558 ) -> Result<(), ExecutionError>
559 where
560 S: StackOperations,
561 H: HeapOperations,
562 {
563 let cloned_handle = heap.alloc_object();
564 let cloned_heap_handle = HeapHandle::<ObjectEntry>::new(cloned_handle);
565 stack.push(Value::Object(cloned_heap_handle));
566 Ok(())
567 }
568
569 /// Deallocates a heap entry
570 ///
571 /// Frees the memory associated with a heap entry.
572 ///
573 /// # Arguments
574 /// * `_stack` - The stack (unused in this operation)
575 /// * `_heap` - The heap containing the entry
576 /// * `_handle` - The handle to deallocate
577 ///
578 /// # Returns
579 /// * `Ok(())` on success
580 /// * `Err(ExecutionError)` on failure
581 pub fn deallocate<S, H>(
582 _stack: &mut S,
583 _heap: &mut H,
584 _handle: HeapHandle<ObjectEntry>,
585 ) -> Result<(), ExecutionError>
586 where
587 S: StackOperations,
588 H: HeapOperations,
589 {
590 Ok(())
591 }
592}